﻿using Modbus.Device;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
using System.Xml;
using WarningAndMonitingSystem.Common;
using WarningAndMonitingSystem.Controls;
using WarningAndMonitingSystem.Models;

namespace WarningAndMonitingSystem
{
    public partial class FrmMain : Form
    {
        private System.Timers.Timer timer;
        private SerialPort serialPort;
        private IModbusMaster master;
        private List<AlarmInfo> alarmInfos = new List<AlarmInfo>();
        private List<ParamInfo> paramInfos = new List<ParamInfo>();
        private List<PumpInfo> pumpInfos = new List<PumpInfo>();
        private List<SlaveInfo> slaveInfos = new List<SlaveInfo>();

        private Dictionary<string, bool> stateMap = new Dictionary<string, bool>();
        private Dictionary<string, ushort> dataMap = new Dictionary<string, ushort>();

        private bool isFrst = true;
        private CancellationTokenSource tokenSource = new CancellationTokenSource();
        private AutoResetEvent resetEvent = new AutoResetEvent(false);
        private Task tCheck = null;

        public FrmMain()
        {
            InitializeComponent();
            // 加载配置信息
            LoadSlaveInfo();
            LoadPumpInfo();
            LoadAlarmInfo();
            LoadParamInfo();
            // 加载水泵控件
            LoadPumpControl();
            // 初始化定时器
            timer = new System.Timers.Timer();
            timer.Interval = 1000;
            timer.AutoReset = true;
            timer.Elapsed += Timer_Elapsed;
        }

        private void LoadPumpControl()
        {
            Control.ControlCollection controls = flowPanel.Controls;
            foreach (PumpInfo pumpInfo in pumpInfos)
            {
                UPump uPump = new UPump();
                uPump.DeviceName = pumpInfo.DeviceName;
                uPump.StateParamName = pumpInfo.StateParamName;
                uPump.FrequencyParamName = pumpInfo.FrequencyParamName;
                uPump.PowerParamName = pumpInfo.PowerParamName;
                uPump.Size = new Size(276, 420);

                controls.Add(uPump);
            }
        }

        private void Timer_Elapsed(object? sender, ElapsedEventArgs e)
        {
            Task.Run(() =>
            {
                ReadAndLoad();
            });
        }

        private void ReadAndLoad()
        {
            // 读取
            ReadDeviceData();

            // 检测报警信息
            if (isFrst)
            {
                isFrst = false;
                // 初始化报警信息列表
                InitAlarmList();
            }
            // 开启监测线程
            CheckAlarmData();

            // 将数据绑定到UI上
            BindDataToUI();
        }

        private void ReadDeviceData()
        {
            foreach (SlaveInfo slaveInfo in slaveInfos)
            {
                List<ParamInfo> paramInfos1 = paramInfos.FindAll(p => p.SlaveId == slaveInfo.SlaveId);
                if (slaveInfo.FunctionCode == 1)
                {
                    try
                    {
                        bool[] flags = master.ReadCoils(slaveInfo.SlaveId, slaveInfo.StartAddress, slaveInfo.Count);

                        foreach (ParamInfo param in paramInfos1)
                        {
                            if (stateMap.ContainsKey(param.ParamName))
                            {
                                stateMap[param.ParamName] = flags[param.Address];
                            }
                            else
                            {
                                stateMap.Add(param.ParamName, flags[param.Address]);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                }
                else if (slaveInfo.FunctionCode == 3)
                {
                    try
                    {
                        ushort[] datas = master.ReadHoldingRegisters(slaveInfo.SlaveId, slaveInfo.StartAddress, slaveInfo.Count);
                        foreach (ParamInfo param in paramInfos1)
                        {
                            if (dataMap.ContainsKey(param.ParamName))
                            {
                                dataMap[param.ParamName] = datas[param.Address];
                            }
                            else
                            {
                                dataMap.Add(param.ParamName, datas[param.Address]);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                }
            }

        }

        private void BindDataToUI()
        {
            bool fState = stateMap.TryGetValue("SystemState", out bool systemState);
            if (fState)
            {
                this.Invoke(new Action(() =>
                {
                    systemStateLb.Text = systemState ? "已启动" : "已停止";
                }));
            }
            foreach (Control control in flowPanel.Controls)
            {
                if (!(control is UPump)) continue;
                UPump? uPump = control as UPump;
                bool f1 = stateMap.TryGetValue(uPump.StateParamName, out bool state);
                if (f1)
                {
                    this.Invoke(new Action(() =>
                    {
                        uPump.IsOn = state;
                    }));
                }

                bool f2 = dataMap.TryGetValue(uPump.FrequencyParamName, out ushort frequency);
                if (f2)
                {
                    this.Invoke(new Action(() =>
                    {
                        uPump.Frequency = frequency;
                    }));
                }

                bool f3 = dataMap.TryGetValue(uPump.PowerParamName, out ushort power);
                if (f3)
                {
                    this.Invoke(new Action(() =>
                    {
                        uPump.Power = power;
                    }));
                }
                // UI层面展示报警信息
                ShowWarningInfo(uPump);
            }
        }

        private void ShowWarningInfo(UPump uPump)
        {
            string frequencyParamName = uPump.FrequencyParamName;
            string powerParamName = uPump.PowerParamName;

            List<AlarmLogInfo> alarmLogInfos = DataContextUtil.GetAlarmLogInfos(p => frequencyParamName.Equals(p.ParamName));

            if (alarmLogInfos.Count > 0)
            {
                AlarmLogInfo freqAlarmInfo = alarmLogInfos[alarmLogInfos.Count - 1];
                if (freqAlarmInfo != null)
                {
                    if (freqAlarmInfo.AlarmType.Equals("Lower"))
                    {
                        if (freqAlarmInfo.AlarmStatus)
                        {
                            uPump.FreqAlarmColors = new Color[] { Color.Blue, Color.LightBlue };
                            uPump.FreqAlarmOn = true;
                        }
                        else
                        {
                            uPump.FreqAlarmColors = new Color[] { Color.Red, Color.Red };
                            uPump.FreqAlarmOn = false;
                        }
                    }
                    else if (freqAlarmInfo.AlarmType.Equals("High"))
                    {
                        if (freqAlarmInfo.AlarmStatus)
                        {
                            uPump.FreqAlarmColors = new Color[] { Color.Red, Color.Pink };
                            uPump.FreqAlarmOn = true;
                        }
                        else
                        {
                            uPump.FreqAlarmColors = new Color[] { Color.Red, Color.Red };
                            uPump.FreqAlarmOn = false;
                        }
                    }
                }
            }


            List<AlarmLogInfo> alarmLogInfos1 = DataContextUtil.GetAlarmLogInfos(p => powerParamName.Contains(p.ParamName));
            if (alarmLogInfos1.Count > 0)
            {
                AlarmLogInfo powerAlarmInfo = alarmLogInfos1[alarmLogInfos1.Count - 1];
                if (powerAlarmInfo != null)
                {
                    if (powerAlarmInfo.AlarmType.Equals("Lower"))
                    {
                        if (powerAlarmInfo.AlarmStatus)
                        {
                            uPump.PowerAlarmColors = new Color[] { Color.Blue, Color.LightBlue };
                            uPump.PowerAlarmOn = true;
                        }
                        else
                        {
                            uPump.PowerAlarmColors = new Color[] { Color.Blue, Color.Blue };
                            uPump.PowerAlarmOn = false;
                        }
                    }
                    else if (powerAlarmInfo.AlarmType.Equals("High"))
                    {
                        if (powerAlarmInfo.AlarmStatus)
                        {
                            uPump.PowerAlarmColors = new Color[] { Color.Red, Color.Pink };
                            uPump.PowerAlarmOn = true;
                        }
                        else
                        {

                            uPump.PowerAlarmColors = new Color[] { Color.Blue, Color.Blue };
                            uPump.PowerAlarmOn = false;
                        }
                    }
                }
            }
        }

        Dictionary<string, int> lastAlarmValues = new Dictionary<string, int>();

        private void InitAlarmList()
        {
            List<ParamInfo> paras = paramInfos.FindAll(p => p.SlaveId == 2);
            foreach (AlarmInfo alarmInfo in alarmInfos)
            {
                List<ParamInfo> paras1 = paras.FindAll(p => p.ParamName.Contains(alarmInfo.Name));
                foreach (var paramInfo in paras1)
                {
                    string key = paramInfo.ParamName + "_" + alarmInfo.Type;
                    if (!lastAlarmValues.ContainsKey(key)
                        && dataMap.ContainsKey(paramInfo.ParamName))
                    {
                        int val = dataMap[paramInfo.ParamName];
                        lastAlarmValues.Add(key, val);
                        if ("Lower".Equals(alarmInfo.Type))
                        {
                            if (val < alarmInfo.Value)
                            {
                                AddAlarmInfo(alarmInfo.Type, paramInfo.ParamName, true, val, paramInfo.Note + "低值报警");
                            }
                        }
                        else if ("High".Equals(alarmInfo.Type))
                        {
                            if (val > alarmInfo.Value)
                            {
                                AddAlarmInfo(alarmInfo.Type, paramInfo.ParamName, true, val, paramInfo.Note + "高值报警");
                            }
                        }
                    }
                }

            }
        }

        int fId = 0;
        private void AddAlarmInfo(string alarmType, string paramName, bool status, int alarmValue, string alarmNote)
        {
            fId++;
            AlarmLogInfo alarmLogInfo = new AlarmLogInfo()
            {
                AlarmNo = fId,
                AlarmTime = DateTime.Now,
                AlarmType = alarmType,
                ParamName = paramName,
                AlarmStatus = status,
                AlarmValue = alarmValue,
                AlarmNote = alarmNote
            };
            DataContextUtil.AddAlarmLogInfo(alarmLogInfo);
            DataContextUtil.UpdateDgView();
        }

        private void CheckAlarmData()
        {
            CancellationToken tk = tokenSource.Token;
            if (tCheck == null)
            {
                tCheck = Task.Run(() =>
                {
                    while (true)
                    {
                        if (tk.IsCancellationRequested)
                        {
                            return;
                        }

                        if (lastAlarmValues.Count > 0)
                        {
                            List<ParamInfo> paras = paramInfos.FindAll(p => p.SlaveId == 2);
                            foreach (AlarmInfo alarmInfo in alarmInfos)
                            {
                                List<ParamInfo> paras1 = paras.FindAll(p => p.ParamName.Contains(alarmInfo.Name));
                                foreach (var paramInfo in paras1)
                                {
                                    string key = paramInfo.ParamName + "_" + alarmInfo.Type;
                                    int oldVal = lastAlarmValues[key];
                                    int newVal = dataMap[paramInfo.ParamName];
                                    int alarmValue = alarmInfo.Value;
                                    if ("Lower".Equals(alarmInfo.Type))
                                    {
                                        // 情况一、之前正常，当前小于最小阈值
                                        if (oldVal > alarmValue && newVal < alarmValue)
                                        {
                                            AddAlarmInfo(alarmInfo.Type, paramInfo.ParamName, true, newVal, paramInfo.Note + "低值报警");
                                        }
                                        // 情况二、之前小于阈值，当前正常
                                        if (oldVal < alarmValue && newVal > alarmValue)
                                        {
                                            AddAlarmInfo(alarmInfo.Type, paramInfo.ParamName, false, newVal, paramInfo.Note + "取消低值报警");
                                        }
                                        // 情况三、一直处于正常状态，不做处理
                                    }
                                    else if ("High".Equals(alarmInfo.Type))
                                    {
                                        // 情况一、之前正常，当前小于最小阈值
                                        if (oldVal < alarmValue && newVal > alarmValue)
                                        {
                                            AddAlarmInfo(alarmInfo.Type, paramInfo.ParamName, true, newVal, paramInfo.Note + "高值报警");
                                        }
                                        // 情况二、之前大于阈值，当前正常
                                        if (oldVal > alarmValue && newVal < alarmValue)
                                        {
                                            AddAlarmInfo(alarmInfo.Type, paramInfo.ParamName, false, newVal, paramInfo.Note + "取消高值报警");
                                        }
                                    }

                                    lastAlarmValues[key] = newVal;
                                }
                            }
                        }

                        resetEvent.WaitOne();
                    }
                }, tk);
            }
            else
            {
                resetEvent.Set();
            }
        }

        private void LoadParamInfo()
        {
            string filePath = "CfgFiles/ParamInfo.xml";
            XmlDocument doc = new XmlDocument();
            doc.Load(filePath);

            XmlElement? root = doc.DocumentElement;
            foreach (XmlNode node in root.ChildNodes)
            {
                ParamInfo paramInfo = new ParamInfo()
                {
                    ParamName = node.SelectSingleNode("ParamName").InnerText,
                    SlaveId = Convert.ToByte(node.SelectSingleNode("SlaveId").InnerText),
                    Address = Convert.ToUInt16(node.SelectSingleNode("Address").InnerText),
                    DataType = node.SelectSingleNode("DataType").InnerText,
                    Note = node.SelectSingleNode("Note").InnerText
                };
                paramInfos.Add(paramInfo);
            }
        }

        private void LoadAlarmInfo()
        {
            string filePath = "CfgFiles/AlarmInfo.xml";
            XmlDocument doc = new XmlDocument();
            doc.Load(filePath);

            XmlElement? root = doc.DocumentElement;
            foreach (XmlNode node in root.ChildNodes)
            {
                AlarmInfo alarmInfo = new AlarmInfo()
                {
                    Name = node.SelectSingleNode("Name").InnerText,
                    Type = node.SelectSingleNode("Type").InnerText,
                    Value = Convert.ToUInt16(node.SelectSingleNode("Value").InnerText)
                };
                alarmInfos.Add(alarmInfo);
            }
        }

        private void LoadPumpInfo()
        {
            string filePath = "CfgFiles/PumpInfo.xml";
            XmlDocument doc = new XmlDocument();
            doc.Load(filePath);

            XmlElement? root = doc.DocumentElement;
            foreach (XmlNode node in root.ChildNodes)
            {
                PumpInfo pumpInfo = new PumpInfo()
                {
                    DeviceName = node.SelectSingleNode("DeviceName").InnerText,
                    StateParamName = node.SelectSingleNode("StateParamName").InnerText,
                    FrequencyParamName = node.SelectSingleNode("FrequencyParamName").InnerText,
                    PowerParamName = node.SelectSingleNode("PowerParamName").InnerText
                };
                pumpInfos.Add(pumpInfo);
            }
        }

        private void LoadSlaveInfo()
        {
            string filePath = "CfgFiles/SlaveInfo.xml";
            XmlDocument doc = new XmlDocument();
            doc.Load(filePath);

            XmlElement? root = doc.DocumentElement;
            foreach (XmlNode node in root.ChildNodes)
            {
                SlaveInfo slaveInfo = new SlaveInfo()
                {
                    SlaveId = Convert.ToByte(node.SelectSingleNode("SlaveId").InnerText),
                    FunctionCode = Convert.ToByte(node.SelectSingleNode("FunctionCode").InnerText),
                    StartAddress = Convert.ToUInt16(node.SelectSingleNode("StartAddress").InnerText),
                    Count = Convert.ToUInt16(node.SelectSingleNode("Count").InnerText)
                };
                slaveInfos.Add(slaveInfo);
            }
        }

        private void Start_Click(object sender, EventArgs e)
        {
            if (serialPort == null)
            {
                serialPort = new SerialPort();
                serialPort.PortName = "COM2";
                serialPort.Parity = Parity.None;
                serialPort.DataBits = 8;
                serialPort.StopBits = StopBits.One;
                serialPort.ReadTimeout = 2000;
            }

            if (!serialPort.IsOpen)
            {
                try
                {
                    serialPort.Open();
                    master = ModbusSerialMaster.CreateRtu(serialPort);
                    timer.Start();
                    startBtn.Text = "停止";
                    systemStateLb.Text = "已启动";
                    statusLb.Text = "已启动";
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            else
            {
                try
                {
                    serialPort.Close();
                    timer.Stop();
                    tokenSource.Cancel();
                    tCheck = null;
                    startBtn.Text = "启动";
                    systemStateLb.Text = "已停止";
                    statusLb.Text = "已停止";
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            AlarmListForm alarmListForm = new AlarmListForm();
            alarmListForm.Show();
        }
    }
}
